<template>
  <view class="muqian-content" :style="{ width, height}">
    <!-- 加载中 -->
    <image
      :src="loadSrc"
      class="muqian-image muqain-load"
      @load="init"
      mode="center"
      :style="{
        opacity: isShow ? '0' : '1',
        borderRadius,
        width,
        height,
        transition: `opacity ${duration / 1000}s ${effect}`,
      }"></image>
    <!-- 加载成功 -->
    <image
      class="muqian-image"
      @load="load"
      @error="error"
      v-if="status == 1"
      :src="src"
      :mode="mode"
      :style="{
        opacity: isShow ? '1' : '0',
        borderRadius,
        width,
        height,
        transition: `opacity ${duration / 1000}s ${effect}`,
      }"></image>
    <!-- 加载失败 -->
    <image
      class="muqian-image"
      v-if="status == 2"
      :src="errorSrc"
      mode="center"
      :style="{
        opacity: isShow ? '1' : '0',
        borderRadius,
        width,
        height,
        transition: `opacity ${duration / 1000}s ${effect}`
      }"></image>
  </view>
</template>

<script setup>
import { ref, watch, nextTick, onUnmounted, getCurrentInstance } from 'vue'
import mitt from 'mitt'
const emitter = mitt()
let loadTimer = null
const emit = defineEmits(['showSuccess', 'show', 'destroyed'])
const props = defineProps({
  mode: {
    type: String,
    default: 'aspectFill'
  },
  borderRadius: {
    type: String,
    default: '0'
  },
  // 图片宽度
  width: {
    type: String,
    default: '100%'
  },
  height: {
    type: String,
    default: '100%'
  },
  // 图片链接
  src: {
    type: String,
    default: ''
  },
  // 当图片加载过快(存在缓存)至少显示多少秒加载动画
  minTimeOut: {
    type: String || Number,
    default: '300'
  },
  // 当图片到屏幕哪个位置的时候开始加载 单位px 可以是负数
  showDistance: {
    type: Object,
    default: () => {
      return {
        bottom: 50
      }
    }
  },
  // 过渡效果  linear / ease / ease-in / ease-out / ease-in-out
  effect: {
    type: String,
    default: 'linear'
  },
  // 图片加载成功后的过渡时间 单位毫秒
  duration: {
    type: String || Number,
    default: '300'
  },
  // 加载中图片
  loadSrc: {
    type: String,
    default: 'https://img.qumoyugo.com/webimgbg/pets-loading-image-mp.png'
  },
  // 加载失败图片
  errorSrc: {
    type: String,
    default: 'https://img.qumoyugo.com/webimgbg/pets-loadfail-mp.png'
  }
})
const status = ref(0)
const isShow = ref(false)
const _this = getCurrentInstance()
const intersectionObserver = uni.createIntersectionObserver(_this.proxy)

watch(
  () => props.src,
  (newValue) => {
    if (!isShow.value) return
    status.value = 0
    isShow.value = false
    nextTick(() => {
      status.value = 1
    })
  },
  { immediate: true }
)
onUnmounted(() => {
  emit('destroyed')
})
const load = () => {
  if (props.minTimeOut === 0) {
    isShow.value = true
  } else {
    const newTimer = new Date().getTime() - loadTimer
    if (newTimer < props.minTimeOut) {
      setTimeout(() => {
        isShow.value = true
      }, props.minTimeOut - newTimer)
    } else {
      isShow.value = true
    }
  }
  setTimeout(() => {
    emit('showSuccess')
  }, props.duration)
}
const error = () => {
  status.value = 2
  isShow.value = true
}
const init = () => {
  let load = false
  // 当图片加载完的时候取消监听
  emitter.on('destroyed', () => {
    intersectionObserver.disconnect()
  })
  intersectionObserver.relativeToViewport(props.showDistance)?.observe('.muqain-load', res => {
    if (!load && res.intersectionRatio === 0) {
      load = true
      return
    }
    emit('show')
    load = true
    status.value = 1
    loadTimer = new Date().getTime()
    intersectionObserver.disconnect()
  })
}
</script>

<style lang="scss" scoped>
.muqian-content {
  overflow: hidden;
  position: relative;
  .muqian-image {
    position: relative;
    display: block;
    will-change: transform;
    z-index: 0;
    background-color: #E9E9E9
  }
  .muqain-load {
    position: absolute;
    left: 0;
    top: 0;
    width: 100rpx;
    height: 100rpx;
    z-index: 1;
  }
}
</style>
